module Visualization

import vis::Figure;
import vis::Render;
import Data;
import util::Math;
import IO;
import List;
import vis::KeySym;
import util::Editors;
import lang::java::jdt::JavaADT;
import lang::java::jdt::Java;
import lang::java::jdt::JDT;

list[RelEntry] classes;
list[Figure] boxes;
list[str] metricChoices =["Lines","Duplication","CC","Unit Size"];

str sizeChoice = "Lines";
str colorChoice = "Duplication";
Color stdColor = rgb(0,255,0);

// Use 0.000001 to avoid division by zero. 
real maxPackageLOC = 0.000001; real maxPackageDUP = 0.000001; real maxPackageCC = 0.000001; real maxPackageUS = 0.000001;
real maxClassLOC = 0.000001; real maxClassDUP = 0.000001; real maxClassCC = 0.000001; real maxClassUS = 0.000001;

int minWidth = 10;
int maxSize = 200;
int maxColor = 255;

public int getWidth(RelEntry entry)
{
	packageMetrics = getPackageMetricsFile();
	switch(sizeChoice)
	{
		// not possible to do like this: case metricChoices[0].
		case "Lines" : {switch(entry){
			case package(_) : return (toInt((packageMetrics[entry.name].LOC / maxPackageLOC) * maxSize)) + minWidth;
		 	default :return toInt((entry.metrics.LOC / maxClassLOC) *maxSize) + minWidth;}}
		case "Duplication" : {switch(entry){
			case package(_) : return (toInt((packageMetrics[entry.name].DUP / maxPackageDUP) * maxSize)) + minWidth;
		 	default :return toInt((entry.metrics.DUP / maxClassDUP) *maxSize) + minWidth;}}
		case "CC" :{switch(entry){
			case package(_) : return (toInt((packageMetrics[entry.name].CC / maxPackageCC) * maxSize)) + minWidth;
		 	default :return toInt((entry.metrics.CC / maxClassCC) *maxSize) + minWidth;}}
		case "Unit Size" :{switch(entry){
			case package(_) : return (toInt((packageMetrics[entry.name].US / maxPackageUS) * maxSize)) + minWidth;
		 	default :return toInt((entry.metrics.US / maxClassUS) *maxSize) + minWidth;}}
	}
	return minwidth;
}

public Color getColor(RelEntry entry)
{
	packageMetrics = getPackageMetricsFile();
	switch(colorChoice)
	{
		// not possible to do like this: case metricChoices[0].
		case "Lines" : {switch(entry){
			case package(_) : return rgb(120,maxColor -(toInt((packageMetrics[entry.name].LOC / maxPackageLOC) * maxColor)),0);
		 	default :return rgb(120,maxColor -toInt((entry.metrics.LOC / maxClassLOC) *maxColor), 0);}}
		case "Duplication" : {switch(entry){
			case package(_) : return rgb(120,maxColor -(toInt((packageMetrics[entry.name].DUP / maxPackageDUP) * maxColor)),0);
		 	default : {println("entry : <entry.metrics.DUP> max <maxClassDUP>");println(", division <toInt((entry.metrics.DUP / maxClassDUP))>");return rgb(120,maxColor -toInt((entry.metrics.DUP / maxClassDUP) *maxColor), 0);}}}
		case "CC" :{switch(entry){
			case package(_) : return rgb(120,maxColor -(toInt((packageMetrics[entry.name].CC / maxPackageCC) * maxColor)),0);
		 	default :return rgb(120,maxColor -toInt((entry.metrics.CC / maxClassCC) *maxColor), 0);}}
		case "Unit Size" :{switch(entry){
			case package(_) : return rgb(120,maxColor -(toInt((packageMetrics[entry.name].US / maxPackageUS) * maxColor)),0);
		 	default :return rgb(120,maxColor -toInt((entry.metrics.US / maxClassUS) *maxColor), 0);}}
	}
	return rgb(0,0,0);
}

public Figure tooltip(RelEntry input)
{
	packageMetrics = getPackageMetricsFile();
	if(package(_) := input)
	{
		return(box(text("<input.name> \nsLOC : <packageMetrics[input.name].LOC> \nDUP: <packageMetrics[input.name].DUP> \nCC :<packageMetrics[input.name].CC> \nUS : <packageMetrics[input.name].US>"),height(20),width(200),resizable(false)));
	}
	else
	{
		return(box(text("<input.name> + \n LOC : <input.metrics.LOC>"),height(20),width(200),resizable(false)));
	}
}

public Figure getMenu(rel[RelEntry,RelEntry] structure, RelEntry current)
{
	return vcat([vcat([text(str(){return "Size : " + sizeChoice ;}, left()),
		choice(metricChoices, void(str s){ sizeChoice = s;drawStructure(structure,current);})], height(100),width(150),resizable(false),fontSize(10))
		, vcat([text(str(){return "Color : " + colorChoice ;}, left()),
					choice(metricChoices, void(str s){ colorChoice = s;drawStructure(structure,current);})], height(100),width(150),resizable(false),fontSize(6))], resizable(false),top(), width(150));
}

public Figure getLegend()
{
	return hcat([text("Package : ",height(20),width(100),resizable(false),fillColor("black")),
			ellipse(height(20),width(100),resizable(false)),
			text("Class : ",height(20),width(100),resizable(false),fillColor("black")),
			box(height(20),width(100),resizable(false)),
			text("Best : ",height(20),width(100),resizable(false),fillColor("black")),
			box(height(20),width(100),resizable(false),fillColor(rgb(120,255,0))),
			text("Worst : ",height(20),width(100),resizable(false),fillColor("black")),
			box(height(20),width(100),resizable(false),fillColor(rgb(120,0,0)))
			],resizable(false), left());
}

public Figure getTopBar(rel[RelEntry,RelEntry] structure, RelEntry current)
{
	Figure backButton = button("Back",void(){drawStructure(structure, [ first | <first,last> <- structure, current := last][0]);},
								width(100),height(20),left(),resizable(false));					
				
	//Get name of the current node.
	str currentNode = "Root";
	if(!(root() := current))
	{
		currentNode = current.name;
	}				
	Figure currentNodeText = text(("Current Node: <currentNode>"),height(30),fillColor(stdColor),left());
    
    return hcat([backButton,currentNodeText], width(300),resizable(false),left());
}

public void drawStructure(rel[RelEntry,RelEntry] structure, RelEntry current)
{
	render("SmallSql Architecture", hcat([
										vcat([
											getTopBar(structure,current),
											getMainScreen(structure,current), 
											getLegend()]), 
														getMenu(structure,current)]));
}

public Figure getMainScreen(rel[RelEntry,RelEntry] structure, RelEntry current)
{
	println("drawing main screen");
	maxPackageLOC = 0.000001; maxPackageDUP = 0.000001; maxPackageCC = 0.000001; maxPackageUS = 0.000001;
    maxClassLOC = 0.000001; maxClassDUP = 0.000001; maxClassCC = 0.000001; maxClassUS = 0.000001;
	
	packageMetrics = getPackageMetricsFile();	set[RelEntry] children = structure[current];
	list[RelEntry] packages= [];
	list[RelEntry] classes = [];
	
	for(RelEntry entry <- children)
	{
		switch(entry)
		{
			case package(_) : { packages += entry;  if(packageMetrics[entry.name].LOC > maxPackageLOC){maxPackageLOC = packageMetrics[entry.name].LOC;}
													if(packageMetrics[entry.name].DUP > maxPackageDUP){maxPackageDUP = packageMetrics[entry.name].DUP;}
													if(packageMetrics[entry.name].CC > maxPackageCC){maxPackageCC = packageMetrics[entry.name].CC;}
													if(packageMetrics[entry.name].US > maxPackageUS){maxPackageUS = packageMetrics[entry.name].US;}}
			case class(_,_,_) : { classes += entry; if(entry.metrics.LOC > maxClassLOC){maxClassLOC = entry.metrics.LOC;}
													if(entry.metrics.DUP > maxClassDUP){maxClassDUP = entry.metrics.DUP;}
													if(entry.metrics.CC > maxClassCC){maxClassCC = entry.metrics.CC;}
													if(entry.metrics.US > maxClassUS){maxClassUS = entry.metrics.US;}}
		}
	}
	
	ellipses = [ellipse(
			 text(package.name)
			,[height(getWidth(package))
			,width(getWidth(package))
			,fillColor(getColor(package))
			//,mouseOver(tooltip(package))
			,onMouseDown(bool (int butnr, map[KeyModifier,bool] modifiers) { 
				drawStructure(structure, package);
				return true;
			})]
			)
		 | package <- packages ];
	println(packages);
	
	list[Figure] boxes = [box(
			[width(getWidth(class))
			,height(getWidth(class))
			,fillColor(getColor(class))
			,mouseOver(tooltip(class))
			,onMouseDown(bool (int butnr, map[KeyModifier,bool] modifiers) { 
				edit(class.location);
				return true;
			})
			])
		 | class <- classes ];
		 
	return pack(ellipses + boxes,gap(5));
}